﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System.IO;
using ZyGame.Avatar;
using System.Collections;

namespace ZyGame.Drawing
{
    public enum PaintBrush
    {
        /// <summary>
        /// 钢笔
        /// </summary>
        Pen = 1,

        /// <summary>
        /// 刷子
        /// </summary>
        Brush = 2,

        /// <summary>
        /// 橡皮檫
        /// </summary>
        Rubber = 3,

        /// <summary>
        /// 拖拽
        /// </summary>
        Drag = 4,
        /// <summary>
        /// 油漆桶
        /// </summary>
        PaintBucket = 5,
    }

    public enum Changed
    {
        WaitSave,
        Saved,
    }

    public sealed class ElementDrawing : IDisposable
    {
        public Changed changed { get; set; } = Changed.Saved;
        public Element element { get; internal set; }
        public DrawingData current { get; private set; }
        public Texture2D original { get; internal set; }
        public RenderTexture render { get; internal set; }
        public ElementComponent component { get; internal set; }
        public RenderTexture drawingRender { get; internal set; }
        public GameObject gameObject { get { return component?.gameObject; } }
        public List<DrawingData> layers { get; private set; } = new List<DrawingData>();
        private Vector3 last_mouse_position = Vector3.zero;
        internal string id;

        private void EnsureSelectionLayer()
        {
            if (current == null)
            {
                throw new Exception("not selection layer");
            }
        }

        public void OnUpdate()
        {
            if (current == null || CameraCtrl.instance.state != ControllerState.Pen)
            {
                return;
            }
            void CheckMouseButtonDown()
            {
                if (!Input.GetMouseButtonDown(0))
                {
                    return;
                }
                if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
                {
                    OnStartDrawing(hit);
                }
            }
            void CheckMouseButtonUp()
            {
                if (!Input.GetMouseButtonUp(0))
                {
                    return;
                }
                OnDrawingCompleted();
            }
            void CheckMouseButtonDrag()
            {
                if (!Input.GetMouseButton(0))
                {
                    return;
                }

                if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
                {
                    OnBeginDarwing(hit);
                }
            }

            CheckMouseButtonDown();
            CheckMouseButtonUp();
            CheckMouseButtonDrag();
        }

        /// <summary>
        /// 绘画
        /// </summary>
        /// <param name="hit"></param>
        private void OnBeginDarwing(RaycastHit hit)
        {
            EnsureSelectionLayer();

            this.changed = Changed.WaitSave;
            if (DrawingSetting.instance.paintBrush == PaintBrush.Drag)
            {
                current.DragDrawing(hit.textureCoord);
                Apply();
                return;
            }

            if (!current.isImport)
            {
                switch (DrawingSetting.instance.paintBrush)
                {
                    case PaintBrush.Rubber:
                        current.Abrasion(hit.textureCoord);
                        break;
                    case PaintBrush.PaintBucket:
                        current.SetColor(hit.textureCoord, DrawingSetting.instance.material.color);
                        break;
                    case PaintBrush.Pen:
                    case PaintBrush.Brush:
                        current.Drawing(hit.textureCoord.x, hit.textureCoord.y);
                        break;
                }
                Apply();
            }
        }


        private void OnStartDrawing(RaycastHit hit)
        {
            if (DrawingSetting.instance.paintBrush == PaintBrush.Drag)
            {
                current.OnStartDrag(hit.textureCoord);
            }
            last_mouse_position = hit.textureCoord;

            DrawingRecord.Start(current.texture);
        }

        private void OnDrawingCompleted()
        {
            DrawingRecord.Ended(current.texture);
            current.OnDrawingCompleted();
        }

        public void Apply()
        {
            render.Clear();
            render.DrawTexture(new Rect(0, 0, original.width, original.height), original, null);
            for (int i = 0; i < layers.Count; i++)
            {
                render.DrawTexture(new Rect(0, 0, layers[i].width, layers[i].height), layers[i].texture, null);
            }
        }

        public void Publishing(string name)
        {
            RenderTexture.active = render;
            Texture2D publishTexture = new Texture2D(current.width, current.height, TextureFormat.ARGB32, false);
            publishTexture.ReadPixels(new Rect(0, 0, current.width, current.height), 0, 0);
            publishTexture.Apply();
            RenderTexture.active = null;
            component.UploadElementData(id, name, publishTexture.EncodeToPNG(), PublishState.Process, args =>
            {
                if (args == null)
                {
                    Client.Events.Notify(EventNames.PUBLISHING_GRAFFITI_COMPLETED, string.Empty);
                    return;
                }
                Client.Events.Notify(EventNames.PUBLISHING_GRAFFITI_COMPLETED, Newtonsoft.Json.JsonConvert.SerializeObject(args));
            });
        }

        public void Save(string name)
        {
            changed = Changed.Saved;
            if (name.IsNullOrEmpty())
            {
                Client.Events.Notify(EventNames.SAVED_GRAFFITI_DATA_COMPLETED, string.Empty);
                return;
            }
            MemoryStream stream = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(stream);
            writer.Write((byte)element);
            writer.Write(Newtonsoft.Json.JsonConvert.SerializeObject(component.elementData));
            writer.Write((byte)layers.Count);
            for (int i = 0; i < layers.Count; i++)
            {
                DrawingData.Write(layers[i], writer);
            }
            component.UploadElementData(id, name, stream.ToArray(), PublishState.Drafts, args =>
            {
                if (args == null)
                {
                    Client.Events.Notify(EventNames.SAVED_GRAFFITI_DATA_COMPLETED, string.Empty);
                    return;
                }
                changed = Changed.Saved;
                Client.Events.Notify(EventNames.SAVED_GRAFFITI_DATA_COMPLETED, Newtonsoft.Json.JsonConvert.SerializeObject(args));
            });
        }

        public void ImportTexture(byte[] textureBytes)
        {
            EnsureSelectionLayer();
            current.Import(textureBytes);
            Apply();
            Client.Events.Notify(EventNames.IMPORT_GRAFFITI_TEXTURE_COMPLETED);
        }

        public void NewLayer(string name)
        {
            if (layers.Count >= 5)
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.LAYER_OUT_LIMIT_COUNT);
                return;
            }

            layers.Insert(0, new DrawingData(original.width, original.height, name));
            SelectionLayer(name);
            current.texture.Clear();
            Apply();
            Client.Events.Notify(EventNames.CREATE_LAYER_COMPLETED, current.name);
        }

        public void ResizeLayer(float size)
        {
            EnsureSelectionLayer();
            OnStartDrawing(default);
            current.Resize(size);
            OnDrawingCompleted();
            Apply();
        }

        public void DeleteLayer()
        {
            EnsureSelectionLayer();
            if (!layers.Contains(current))
            {
                return;
            }

            string name = current.name;
            layers.Remove(current);
            current = layers.LastOrDefault();
            Apply();
            Client.Events.Notify(EventNames.DELETE_LAYER_COMPLETED, name);
        }

        public void SelectionLayer(string name)
        {
            DrawingData layer = layers.Find(x => x.name == name);
            if (layer == null)
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, string.Format(ErrorInfo.NOT_FIND_TATH_LAYER, name));
                return;
            }

            current = layer;
        }

        public void SetLayerAlpha(float alpha)
        {
            EnsureSelectionLayer();
            current.SetAlpha(alpha);
            Apply();
        }

        public void Dispose()
        {
            Client.Tools.RemoveCallback(OnUpdate);
            this.gameObject.DestroyMeshCollider();
            this.component.SetElementTexture(original);
            Client.Avatar.EnableElement((int)Element.None);
        }

        public void ChangeLayer(int index)
        {
            if (current == null)
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_FIND_TATH_LAYER);
                return;
            }
            if (index < 0 || index >= layers.Count)
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, "ArgumentOutOfRangeException");
                return;
            }
            layers.Remove(current);
            layers.Insert(index, current);
            Apply();
            Client.Events.Notify(EventNames.SORT_LAYER_SUCCESSFLY, "");
        }
    }
}